
#include "CalculatorForm.h"

#include "FUiLayout.h"
#include "FUiRelativeLayout.h"
#include "FUiGridLayout.h"

using namespace Osp::Base;
using namespace Osp::Graphics;
using namespace Osp::Ui;
using namespace Osp::Ui::Controls;
using namespace Osp::Base::Collection;


//////////////////////////////////////////////////////////////////////////
// CalculatorForm

CalculatorForm::CalculatorForm(void) :
	__pGridPanel(null), __pLabelPrint(null) 
{
}


CalculatorForm::~CalculatorForm(void) 
{
}


result
CalculatorForm::Construct(void)
{

	RelativeLayout relLayout;
	relLayout.Construct();

	Form::Construct(relLayout, FORM_STYLE_INDICATOR | FORM_STYLE_HEADER);

	Header * pHeader = GetHeader();
	if (pHeader)
	{
		pHeader->SetStyle(HEADER_STYLE_TITLE);
		pHeader->SetTitleText("Calculator");
	}

	return E_SUCCESS;
}


result
CalculatorForm::OnInitializing(void)
{
	GridLayout gridLayout;
	gridLayout.Construct(6, 4);

	Panel* pGridPanel = new Panel;
	pGridPanel->Construct(gridLayout, Rectangle(0, 0, 350, 540));

	this->AddControl(*pGridPanel);

	RelativeLayout* relLayout = dynamic_cast<RelativeLayout*>(this->GetLayoutN());

	relLayout->SetCenterAligned(*pGridPanel, CENTER_ALIGN_HORIZONTAL);
	relLayout->SetCenterAligned(*pGridPanel, CENTER_ALIGN_VERTICAL);

	__pGridPanel     = pGridPanel;

	/* Create Buttons */
	CreateButtons();

	this->Draw();

	return E_SUCCESS;
}


result
CalculatorForm::OnTerminating(void)
{
	return E_SUCCESS;
}


result
CalculatorForm::OnDraw(void)
{

	return E_SUCCESS;
}


void
CalculatorForm::OnActionPerformed(const Osp::Ui::Control& source, int actionId)
{
	switch(actionId)
	{
		// numbers
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
			__Calculator.isOpJustOn = false;

			if(__Calculator.IsFinished() == true || __Calculator.IsOperator() == true)
			{
				__strBufferForPrint.Clear();
			}

			if(__strBufferForPrint.GetLength() >= MAX_PRINTABLE_CHARS)	// If the length exceeds MAX_PRINTABLE_CHARS, dot dot dot will be shown.
			{
				return;
			}			

			__strBufferForPrint.Append(actionId);
			RefineNumber(__strBufferForPrint);

			SetOperandByContext(__strBufferForPrint);

			__pLabelPrint->SetText(__strBufferForPrint);
			__pLabelPrint->SetTextHorizontalAlignment(ALIGNMENT_RIGHT);
			__pLabelPrint->Draw();
			break;

		case ID_BTN_PLUS:
		case ID_BTN_MINUS:
		case ID_BTN_MULTIPLY:
		case ID_BTN_DIVIDE: // operators
			{
				__Calculator.isOpJustOn = true;

				if(__Calculator.IsReady() == true)
				{
					// do nothing
					return;
				}
				else if( __Calculator.IsOperand1() == true || __Calculator.IsOperator() == true || __Calculator.IsFinished() == true)
				{
					__Calculator.SetOperator(actionId);
				}				
				else if(__Calculator.IsOperand2() == true)
				{
					RefineNumber(__strBufferForPrint);
					__Calculator.SetOperand2(__strBufferForPrint);
					String strResult;
					__Calculator.Calc(strResult);

					__strBufferForPrint = strResult;
					RefineNumber(__strBufferForPrint);

					__Calculator.SetOperand1(strResult);
					__Calculator.SetOperator(actionId);

					__pLabelPrint->SetText(__strBufferForPrint);
					__pLabelPrint->SetTextHorizontalAlignment(ALIGNMENT_RIGHT);
					__pLabelPrint->Draw();
				}
				else
				{
					AppAssert(false);
				}				
			}
			break;
		case ID_BTN_EQUAL:
			{			
				__Calculator.isOpJustOn = false;

				String strResult;
				__Calculator.Calc(strResult);

				__strBufferForPrint = strResult;
				RefineNumber(__strBufferForPrint);

				__pLabelPrint->SetText(__strBufferForPrint);
				__pLabelPrint->SetTextHorizontalAlignment(ALIGNMENT_RIGHT);
				__pLabelPrint->Draw();
			}
			break;
		case ID_BTN_BACK:
			__Calculator.isOpJustOn = false;

			if(__Calculator.IsOperand1() == true || __Calculator.IsOperand2() == true)
			{
				if(__strBufferForPrint.GetLength() > 0)
					__strBufferForPrint.Remove(__strBufferForPrint.GetLength()-1, 1);

				RefineNumber(__strBufferForPrint);

				SetOperandByContext(__strBufferForPrint);

				__pLabelPrint->SetText(__strBufferForPrint);
				__pLabelPrint->SetTextHorizontalAlignment(ALIGNMENT_RIGHT);
				__pLabelPrint->Draw();
			}
			break;
		case ID_BTN_CLEAR:
			__Calculator.isOpJustOn = false;
			__strBufferForPrint.Clear();
			__Calculator.Reset();
			__pLabelPrint->SetText(L"0");
			__pLabelPrint->SetTextHorizontalAlignment(ALIGNMENT_RIGHT);
			__pLabelPrint->Draw();
			break;
		case ID_BTN_DOT:
			{
				if(__Calculator.IsFinished() == true || __Calculator.IsOperator() == true)
				{
					__strBufferForPrint.Clear();
				}

				int nIndexOfDot;
				if(__strBufferForPrint == L"")
				{
					__strBufferForPrint = L"0.";
				}
				else if(__Calculator.isOpJustOn)
					__strBufferForPrint = L"0.";
				else if(IsFailed(__strBufferForPrint.IndexOf(L".", 0, nIndexOfDot)) && __strBufferForPrint.GetLength() != 0)
				{
					__strBufferForPrint += L".";
				}	

				SetOperandByContext(__strBufferForPrint);

				__pLabelPrint->SetText(__strBufferForPrint);
				__pLabelPrint->SetTextHorizontalAlignment(ALIGNMENT_RIGHT);
				__pLabelPrint->Draw();
			}
			break;
	}

	Show();
}


void
CalculatorForm::RefineNumber(String & strNumber,bool isSwapSign /*= false*/)
{
	mchar mch;

	String strSign;

	// for swapping a sign
	if(isSwapSign == true && strNumber != L"0" && strNumber.GetLength() > 0)
	{
		strNumber.GetCharAt(0, mch);
		if(mch == L'-')
		{
			strNumber.Remove(0, 1);
		}
		else
		{
			strSign = L"-";		// will be added
		}
	}

	// Check empty
	if(strNumber.IsEmpty())
	{
		strNumber = L"0";
		return;
	}

	// remove first zero
	if(strNumber.GetLength() > 1 )
	{
		strNumber.GetCharAt(0, mch);
		if( mch == L'0' )
		{
			int nIndex = 0;
			if( IsFailed(strNumber.IndexOf(L".", 0, nIndex)) ) 
			{
				strNumber.Remove(0, 1);
			}
		}
	}

	// assign its sign
	strNumber = strSign + strNumber;

	if(strNumber == L"-" || strNumber == L"+" || strNumber == L"-0")
	{
		strNumber = L"0";
	}
}

void
CalculatorForm::CreateButtons(void)
{
	GridLayout* pLayout = dynamic_cast<GridLayout*>(__pGridPanel->GetLayoutN());

	GridLayout& layout = *pLayout;
	Panel& panel  = *__pGridPanel;

	Button * pButton = null;

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"0");
	pButton->SetName(L"ID_BTN_0");
	pButton->SetActionId(0);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 5, 0, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"1");
	pButton->SetName(L"ID_BTN_1");
	pButton->SetActionId(1);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 4, 0, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"2");
	pButton->SetName(L"ID_BTN_2");
	pButton->SetActionId(2);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 4, 1, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"3");
	pButton->SetName(L"ID_BTN_3");
	pButton->SetActionId(3);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 4, 2, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"4");
	pButton->SetName(L"ID_BTN_4");
	pButton->SetActionId(4);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 3, 0, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"5");
	pButton->SetName(L"ID_BTN_5");
	pButton->SetActionId(5);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 3, 1, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"6");
	pButton->SetName(L"ID_BTN_6");
	pButton->SetActionId(6);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 3, 2, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"7");
	pButton->SetName(L"ID_BTN_7");
	pButton->SetActionId(7);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 2, 0, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"8");
	pButton->SetName(L"ID_BTN_8");
	pButton->SetActionId(8);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 2, 1, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"9");
	pButton->SetName(L"ID_BTN_9");
	pButton->SetActionId(9);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 2, 2, 1, 1);



	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 170, 62), L"Back");
	pButton->SetName(L"ID_BTN_BACK");
	pButton->SetActionId(ID_BTN_BACK);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 1, 0, 1, 2);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 170, 62), L"Clear");
	pButton->SetName(L"ID_BTN_CLEAR");
	pButton->SetActionId(ID_BTN_CLEAR);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 1, 2, 1, 2);


	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L".");
	pButton->SetName(L"ID_BTN_DOT");
	pButton->SetActionId(ID_BTN_DOT);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 5, 1, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"=");
	pButton->SetName(L"ID_BTN_EQUAL");
	pButton->SetActionId(ID_BTN_EQUAL);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 5, 2, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"+");
	pButton->SetName(L"ID_BTN_PLUS");
	pButton->SetActionId(ID_BTN_PLUS);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 5, 3, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"/");
	pButton->SetName(L"ID_BTN_DIVIDE");
	pButton->SetActionId(ID_BTN_DIVIDE);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 2, 3, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"*");
	pButton->SetName(L"ID_BTN_MULTIPLY");
	pButton->SetActionId(ID_BTN_MULTIPLY);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 3, 3, 1, 1);

	pButton = new Button();
	pButton->Construct(Rectangle(0, 0, 80, 62), L"-");
	pButton->SetName(L"ID_BTN_MINUS");
	pButton->SetActionId(ID_BTN_MINUS);
	pButton->AddActionEventListener(*this);
	panel.AddControl(*pButton);
	layout.SetPosition(*pButton, 4, 3, 1, 1);

	__pLabelPrint = new Label();
	__pLabelPrint->Construct(Rectangle(0, 0, 350, 81), L"");
	__pLabelPrint->SetName(L"ID_LABEL_PRINT");

	RefineNumber(__strBufferForPrint);
	__pLabelPrint->SetText(__strBufferForPrint);
	__pLabelPrint->SetTextHorizontalAlignment(ALIGNMENT_RIGHT);
	panel.AddControl(*__pLabelPrint);
	layout.SetPosition(*__pLabelPrint, 0, 0, 1, 4);

	for (int i = 0; i<4; i++)
		layout.SetColumnSpacing(i,10);
	for (int i = 0; i<6; i++)
		layout.SetRowSpacing(i,28);
}


void
CalculatorForm::SetOperandByContext(const String & strOperand)
{
	if(__Calculator.IsFinished() == true || __Calculator.IsReady() == true || __Calculator.IsOperand1() == true)
	{
		__Calculator.SetOperand1(strOperand);
	}
	else if(__Calculator.IsOperator() == true || __Calculator.IsOperand2() == true)
	{
		__Calculator.SetOperand2(strOperand);
	}
}



//////////////////////////////////////////////////////////////////////////
// CalculatorModel

void
CalculatorForm::CalculatorModel::SetOperand1(const String & strOperand)
{
	__strOperand1 = strOperand;
	__CalcMode = enMODE_OPERAND1;
}


void
CalculatorForm::CalculatorModel::SetOperand2(const String & strOperand)
{
	__strOperand2 = strOperand;
	__CalcMode = enMODE_OPERAND2;
}


void
CalculatorForm::CalculatorModel::SetOperator(int nType)
{
	switch(nType)
	{
		case ID_BTN_DIVIDE:
		case ID_BTN_MINUS:
		case ID_BTN_PLUS:
		case ID_BTN_MULTIPLY:
			__nOperator = nType;
			__CalcMode = enMODE_OPERATOR;
			break;
		default:
			AppAssert(false);
			__CalcMode = enMODE_ERROR;
			break;
	}
}


result
CalculatorForm::CalculatorModel::Calc(String & strResult)
{
	if(IsReady() == true )
	{
		return E_SUCCESS;
	}
	else if(IsOperand1() == true)
	{
		strResult = __strOperand1;
		return E_SUCCESS;
	}
	else if(IsOperator() == true)
	{
		__strOperand2 = __strOperand1;
	}

	double dOp1;
	double dOp2;

	result r = Double::Parse(__strOperand1, dOp1);
	if(IsFailed(r))
	{
		strResult = GetErrorMessage(r);
		return r;
	}

	r = Double::Parse(__strOperand2, dOp2);
	if(IsFailed(r))
	{
		strResult = GetErrorMessage(r);
		return r;
	}

	const int MAX_BUFF_SIZE = 1024;

	switch(__nOperator)
	{
		case ID_BTN_PLUS:
			strResult.Format(MAX_BUFF_SIZE, L"%f", dOp1 + dOp2);
			break;
		case ID_BTN_MINUS:
			strResult.Format(MAX_BUFF_SIZE, L"%f", dOp1 - dOp2);
			break;
		case ID_BTN_MULTIPLY:
			if(dOp1 == 0 || dOp2 == 0)
			{
				strResult = L"0";
			}
			else
			{
				strResult.Format(MAX_BUFF_SIZE, L"%f", dOp1 * dOp2);
			}
			break;
		case ID_BTN_DIVIDE:
			strResult.Format(MAX_BUFF_SIZE, L"%f", dOp1 / dOp2);
			break;
		default:
			AppAssert(false);
			__CalcMode = enMODE_ERROR;
			return E_FAILURE;
	}

	// remove last zeros after dot
	int nIndexOf = 0;
	mchar mch;
	if(strResult.IndexOf(L'.', 0, nIndexOf) == E_SUCCESS)
	{
		for(int i = strResult.GetLength(); i > nIndexOf; i--)
		{
			strResult.GetCharAt(i-1, mch);
			if(mch == L'0' || mch== L'.')
			{
				strResult.Remove(i-1, 1);
			}
			else break;
		}
	}

	__strOperand1 = strResult;

	__strBackupResult = strResult;
	__CalcMode = enMODE_FINISHED;

	return E_SUCCESS;
}


void
CalculatorForm::CalculatorModel::Reset()
{
	__strOperand1.Clear();
	__strOperand2.Clear();
	__strBackupResult.Clear();
	__nOperator = ID_BTN_UNKNOWNOP;
	__CalcMode = enMODE_READY;
}


bool
CalculatorForm::CalculatorModel::IsReady() const
{
	return (__CalcMode == enMODE_READY) ? true : false;
}


bool
CalculatorForm::CalculatorModel::IsOperand1() const
{
	return (__CalcMode == enMODE_OPERAND1) ? true : false;
}


bool	CalculatorForm::CalculatorModel::IsOperator() const
{
	return (__CalcMode == enMODE_OPERATOR) ? true : false;
}


bool
CalculatorForm::CalculatorModel::IsOperand2() const
{
	return (__CalcMode == enMODE_OPERAND2) ? true : false;
}


bool
CalculatorForm::CalculatorModel::IsFinished() const
{
	return (__CalcMode == enMODE_FINISHED) ? true : false;
}

